package api

import (
	"encoding/base64"
	"encoding/json"
	"strconv"
	"time"
	
	"github.com/gin-gonic/gin"
	
	"collecter/app/cache"
	"collecter/app/common/enum"
	"collecter/app/common/helper"
	"collecter/app/common/service"
	"collecter/app/controller"
	"collecter/app/model"
)

type AuthorizeController struct {
	controller.BaseController
}

/*
 *  Login
 * @Description: 用户登录
 * @receiver c
 * @param ctx
 */
func (c *AuthorizeController) Login(ctx *gin.Context) {
	type inputType struct {
		Account  string `json:"account,omitempty"`
		AuthType string `json:"auth_type,omitempty"` // 鉴权方式：vcdoe、password
		VCode    int    `json:"vcode,omitempty"`
		Password string `json:"password,omitempty"`
	}
	var inputData inputType
	if err := controller.Ctx.BindJSON(&inputData); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	if len(inputData.Account) == 0 ||
		("vcode" != inputData.AuthType && "password" != inputData.AuthType) ||
		("vcode" == inputData.AuthType && inputData.VCode == 0) ||
		("password" == inputData.AuthType && len(inputData.Password) == 0) {
		c.FormatReturn(6, "")
	}
	auth := service.NewAuthorize().
		WithAccount(inputData.Account).
		WithAuthType(inputData.AuthType).
		WithVcode(inputData.VCode).
		WithPassword(inputData.Password)
	if helper.RegExpVerify(inputData.Account, "mobile") {
		auth.WithAccountType("mobile")
	} else if helper.RegExpVerify(inputData.Account, "email") {
		auth.WithAccountType("email")
	} else {
		auth.WithAccountType("username")
		if len(inputData.Password) == 0 || "vcode" == inputData.AuthType {
			c.FormatReturn(6, "")
		}
	}
	if info, err := auth.Login(); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	} else {
		c.FormatReturn(0, info)
	}

}

/*
 *  Logout
 * @Description: 退出登录
 * @receiver c
 * @param ctx
 */
func (c *AuthorizeController) Logout(ctx *gin.Context) {
	token := c.GetToken()
	if len(token) == 0 {
		c.FormatReturn(4, "")
	}
	var refreshToken string
	if val, err := c.GetRefreshToken(); err != nil {
		c.FormatReturn(enum.FAILED, "", "获取 refresh_token 错误："+err.Error())
	} else {
		refreshToken = val
	}
	var userInfo model.Users
	userInfo.RefreshToken = refreshToken
	userInfo.Status = enum.STATUS_ENABLED

	if err := helper.Db.Where(&userInfo).First(&userInfo).Error; err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	userInfo.RefreshToken = ""
	userInfo.TokenExpired = 0

	if err := helper.Db.Save(&userInfo).Error; err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}

	cacheKey := enum.USER_LOGIN_INFO + refreshToken
	_ = cache.Cacher.Delete(cacheKey)
	cacheKey = enum.ACCESS_TOKEN + token
	_ = cache.Cacher.Delete(cacheKey)

	// 触发用户退出登录事件
	helper.EventTrigger(enum.EVENT_USER_LOGOUT, userInfo.Id, "")

	c.FormatReturn(0, "")
}

/*
 *  Register
 * @Description: 用户注册
 * @receiver c
 * @param ctx
 */
func (c *AuthorizeController) Register(ctx *gin.Context) {
	type inputType struct {
		Account    string `json:"account,omitempty"`
		AuthType   string `json:"auth_type,omitempty"` // 鉴权方式：vcdoe、password
		VCode      int    `json:"v_code,omitempty"`
		Password   string `json:"password,omitempty"`
		RePassword string `json:"re_password,omitempty"`
	}
	var inputData inputType
	if err := controller.Ctx.BindJSON(&inputData); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	if (!helper.RegExpVerify(inputData.Account, "mobile") &&
		!helper.RegExpVerify(inputData.Account, "email") && "password" != inputData.AuthType) ||
		("vcode" != inputData.AuthType && "password" != inputData.AuthType) {
		c.FormatReturn(6, "")
	}
	if len(inputData.Account) == 0 {
		c.FormatReturn(enum.FAILED, "", "账号不能为空")
	}
	if "vcode" == inputData.AuthType && inputData.VCode == 0 {
		c.FormatReturn(enum.FAILED, "", "验证码错误")
	}
	if "password" == inputData.AuthType && len(inputData.Password) == 0 {
		c.FormatReturn(enum.FAILED, "", "密码不能为空")
	}
	if inputData.Password != inputData.RePassword {
		c.FormatReturn(enum.FAILED, "", "两次输入密码不一致")
	}

	auth := service.NewAuthorize().
		WithAccount(inputData.Account).
		WithAuthType(inputData.AuthType).
		WithVcode(inputData.VCode).
		WithPassword(inputData.Password)
	if err := auth.Register(); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	c.FormatReturn(0, "")
}

/**
*  SendVcode
*  @Description: 发送验证码统一接口
*  @receiver c
 */
func (c *AuthorizeController) SendVcode(ctx *gin.Context) {
	type inputType struct {
		Event   string `json:"event,omitempty"`
		Account string `json:"account,omitempty"`
	}
	var inputData inputType
	if err := controller.Ctx.BindJSON(&inputData); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	if len(inputData.Event) == 0 || len(inputData.Account) == 0 {
		c.FormatReturn(6, "")
	}
	vcode := service.NewVcode(inputData.Account, inputData.Event)
	if err := vcode.SendVcode(); err != nil {
		c.FormatReturn(1, "", err.Error())
	}
	c.FormatReturn(enum.SUCCESS, "")
}

/*
 *  RefersToken
 * @Description: 刷新 token
 * @receiver c
 * @param ctx
 */
func (c *AuthorizeController) RefersToken(ctx *gin.Context) {
	refreshToken := controller.Ctx.GetHeader("X-REFRESHTOKEN")
	if len(refreshToken) == 0 {
		c.FormatReturn(6, "")
	}
	var user model.Users
	cacheKey := enum.USER_LOGIN_INFO + refreshToken
	if err := cache.Cacher.Has(cacheKey); err != nil {
		c.FormatReturn(enum.TOKEN_INVALID, "", "token 失效")
	}
	if val, err := cache.Cacher.Get(cacheKey); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	} else {
		if err := json.Unmarshal(val, &user); err != nil {
			c.FormatReturn(enum.FAILED, "", err.Error())
		}
	}
	if user.Id == 0 {
		c.FormatReturn(enum.FAILED, "", "用户缓存数据异常")
	}
	timestamp := time.Now().Unix()
	accessToken := helper.GenerateToken(strconv.Itoa(int(user.Id)), "access_token")
	accessExpire := int64(enum.ACCESS_TOKEN_EXPIRE)
	diffTime := user.TokenExpired - timestamp
	if diffTime < accessExpire {
		accessExpire = diffTime
	}
	cahceKey := enum.ACCESS_TOKEN + accessToken
	if err := cache.Cacher.Set(cahceKey, refreshToken, int(accessExpire)); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	var info service.LIType
	info.AccessToken = accessToken
	info.AccessExpire = timestamp + accessExpire
	info.RefreshToken = refreshToken
	info.RefreshExpire = user.TokenExpired
	c.FormatReturn(enum.SUCCESS, info)
}

// Activate
//
//	@Description: 账号激活，传入参数必须包含 账号、激活号、注册时间
//	@receiver c
//	@param ctx
func (c *AuthorizeController) Activate(ctx *gin.Context) {
	var pm string
	if val, ok := controller.Ctx.GetQuery("pm"); !ok {
		c.FormatReturn(6, "")
	} else {
		pm = val
	}
	var pmByte []byte

	var pms service.PmType
	if val, err := base64.StdEncoding.DecodeString(pm); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	} else {
		pmByte = val
	}
	if err := json.Unmarshal(pmByte, &pms); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	if len(pms.Account) == 0 || len(pms.ActivateCode) == 0 || pms.RegisterTime == "0" {
		c.FormatReturn(6, "")
	}

	if err := pms.AccountActivate(); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	c.FormatReturn(enum.SUCCESS, "")
}

// Bind
//
//	@Description: 绑定账号，可以是手机号或者邮箱账号
//	@receiver c
//	@param ctx
func (c *AuthorizeController) Bind(ctx *gin.Context) {
	type inType struct {
		Account         string `json:"account,omitempty"`
		Password        string `json:"password,omitempty"`
		SendCodeAccount string `json:"send_code_account,omitempty"`
		Vcode           int    `json:"vcode,omitempty"`
	}
	var inData inType
	if err := controller.Ctx.BindJSON(&inData); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	if len(inData.Account) == 0 || inData.Vcode == 0 || len(inData.SendCodeAccount) == 0 || len(inData.Password) == 0 {
		c.FormatReturn(6, "")
	}
	authorize := service.NewAuthorize().WithAccount(inData.Account).WithVcode(inData.Vcode).WithPassword(inData.Password)
	if err := authorize.Bind(inData.SendCodeAccount); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	c.FormatReturn(enum.SUCCESS, "")
}

func (c *AuthorizeController) ResetPassword(ctx *gin.Context) {
	type inType struct {
		Account    string `json:"account,omitempty"`
		Vcode      int    `json:"vcode,omitempty"`
		Password   string `json:"password,omitempty"`
		RePassword string `json:"re_password,omitempty"`
	}
	var inData inType
	if err := controller.Ctx.BindJSON(&inData); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	if len(inData.Account) == 0 || inData.Vcode == 0 || len(inData.Password) == 0 || inData.Password != inData.RePassword {
		c.FormatReturn(6, "")
	}
	auth := service.NewAuthorize().WithAccount(inData.Account).WithVcode(inData.Vcode).WithPassword(inData.Password)
	if err := auth.ResetPassword(); err != nil {
		c.FormatReturn(enum.FAILED, "", err.Error())
	}
	c.FormatReturn(enum.SUCCESS, "")
}
